package com.rui.study.micro.userself.service.impl;

import com.rui.study.commons.model.BizException;
import com.rui.study.commons.model.Result;
import com.rui.study.commons.support.ZKLock;
import com.rui.study.commons.util.MD5Util;
import com.rui.study.commons.util.RandomUtil;
import com.rui.study.commons.util.StringUtil;
import com.rui.study.micro.userself.external.api.KeyApi;
import com.rui.study.micro.userself.mapper.AccountMapper;
import com.rui.study.micro.userself.model.AccountPo;
import com.rui.study.micro.userself.model.RegisterAccountDTO;
import com.rui.study.micro.userself.service.AccountService;
import com.rui.study.micro.userself.util.RedisLock;
import com.rui.study.micro.userself.util.StatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.rui.study.micro.userself.util.Constant.REGISTER_VERIFY_CODE;
import static com.rui.study.micro.userself.util.Constant.USERNAME_NUMBER;

@Service
public class AccountServiceImpl implements AccountService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private KeyApi keyApi;

    @Resource(name = "redisTemplate")
    private ValueOperations<String, String> redisOps;

    @Resource(name = "redisTemplate")
    private HashOperations<Serializable, String, String> hashRedisOps;

    @Autowired
    private RedisLock lock;

    @Autowired
    @Qualifier("serviceDataSource")
    private DataSource dataSource;

//    private Lock lock = new ReentrantLock();

    @Autowired
    private ZKLock zkLock;


    @Override
    public void sendVerifyCode(String account) {
        String verifyCode = RandomUtil.nextDigital(6);
        redisOps.set(REGISTER_VERIFY_CODE + account, verifyCode, 600, TimeUnit.SECONDS);
        LOGGER.info("Succeed to send verify-code [{}] to [{}]", verifyCode, account);
    }

    @Override
    public void registerAccount(RegisterAccountDTO registerAccountDTO) {
        /* 1.校验验证码 */
        String account = registerAccountDTO.getAccount();
        String verifyCode = redisOps.get(REGISTER_VERIFY_CODE + account);
        if (!registerAccountDTO.getVerifyCode().equals(verifyCode)
                && !"888888".equals(registerAccountDTO.getVerifyCode()))
            throw new BizException(StatusCode.VERIFY_CODE_IS_WRONG);

//        Connection connection = null;
//        try {
//            connection = dataSource.getConnection();
//        } catch (SQLException e) {
//            throw new BizException(StatusCode.SYSTEM_EXCEPTION);
//        }
//        MySqlLock lock = new MySqlLock(connection);

//        lock.lock("registerAccount");

        zkLock.lock("registerAccount");
        try {
            /* 2.校验账号是否可用 */
            AccountPo accountPoInDB = accountMapper.selectAccount(account);
            if (accountPoInDB != null) {
                throw new BizException(StatusCode.ACCOUNT_ALREADY_IN_USED);
            }

            /* 3.生成账号ID和用户名 */
            String accountId = RandomUtil.nextUserId();
            String username = defaultUsername();

            /* 4.获取盐值 */
            Result<String> _result_ = keyApi.getSalt(accountId);
            if (_result_.hasError()) {
                throw new BizException(_result_.getStatus(), _result_.getMessage());
            }
            String salt = _result_.getData();
            String password = registerAccountDTO.getPassword();

            /* 5.持久化对象 */
            AccountPo accountPo = new AccountPo();
            accountPo.setAccount_id(accountId);
            accountPo.setUsername(username);
            if (StringUtil.isEmail(registerAccountDTO.getAccount())) {
                accountPo.setEmail(account);
            } else if (StringUtil.isPhoneNumber(registerAccountDTO.getAccount())) {
                accountPo.setCellphone(account);
            } else {
                throw new BizException(StatusCode.NOT_SUPPORT_ACCOUNT);
            }
            accountPo.setPassword(MD5Util.encrypt(password + salt));
            int result = accountMapper.insertAccount(accountPo);
            if (result < 1)
                throw new BizException(StatusCode.INSERT_EXCEPTION);
            LOGGER.info("Succeed to add account [{}]", accountPo.getAccount_id());
        } finally {
//            lock.unlock();
//            lock.unlock("registerAccount");
            zkLock.unlock("registerAccount");
        }

        /* 6.移除验证码 */
        if (redisOps.getOperations().delete(REGISTER_VERIFY_CODE + account))
            LOGGER.info("Succeed to delete the cached verify-code for [{}]", account);
    }

    private String defaultUsername() {

        String date = new SimpleDateFormat("yyyyMMdd").format(new Date());

        long count = hashRedisOps.increment(USERNAME_NUMBER, date, 1);
        if (count > 999999)
            throw new BizException(StatusCode.USERNAME_MAXIMUM_NUMBER_REACHED);

        String number = new DecimalFormat("000000").format(count);

        return "RuiStudy_" + date + number;
    }

    @Override
    public Result queryAccount(String identifier) {
        AccountPo accountPo = accountMapper.selectAccount(identifier);
        return new Result(StatusCode.SUCCEED, accountPo);
    }

    @Override
    public List<AccountPo> queryAccountList() {
        return null;
    }

}
